using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using QiHe.CodeGen.Aspx;
using System.Xml.Serialization;

namespace QiHe.ReliableData
{
    public class XmlHead : XmlNode
    {
        public XmlHead()
            : base("xml")
        {
            SetAttribute("version", "1.0");
            SetAttribute("encoding", "utf-8");
        }

        public string Text
        {
            get
            {
                return String.Format("<?{0}?>", GetHeader());
            }
        }

        public static XmlHead Default = new XmlHead();
    }
    public class XmlSerialization
    {
        public static void Serialize(string xmlfile, object data)
        {
            XmlNode node = CreateXmlNode(data);
            using (StreamWriter writer = new StreamWriter(xmlfile, false, Encoding.UTF8))
            {
                writer.Write(XmlHead.Default.Text);
                node.Output(writer, 2);
            }
        }

        public static XmlNode CreateXmlNode(object data)
        {
            Type type = data.GetType();
            if (TypeHelper.IsGenericList(type))
            {
                Type itemType = type.GetGenericArguments()[0];
                return CreateXmlNode("ArrayOf" + itemType.Name, data, false);
            }
            else
            {
                return CreateXmlNode(type.Name, data, false);
            }
        }

        public static XmlNode CreateXmlNode(string name, object data, bool isTransparent)
        {
            XmlNode node = new XmlNode(name);
            node.IsTransparent = isTransparent;
            Type type = data.GetType();
            if (TypeHelper.IsAtomType(type))
            {
                if (data is bool)
                {
                    node.Value = data.ToString().ToLower();
                }
                else
                {
                    node.Value = data.ToString();
                }
            }
            else
            {
                if (TypeHelper.IsGenericList(type))
                {
                    foreach (object item in (IEnumerable)data)
                    {
                        node.AddChild(CreateXmlNode(item));
                    }
                }
                else
                {
                    foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
                    {
                        object[] attributes = fi.GetCustomAttributes(false);
                        object value = fi.GetValue(data);
                        Serialize(fi.Name, value, node, attributes);
                    }
                    foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
                    {
                        object[] attributes = pi.GetCustomAttributes(false);
                        object value = pi.GetValue(data, null);
                        Serialize(pi.Name, value, node, attributes);
                    }
                }
            }
            return node;
        }

        private static void Serialize(string name, object value, XmlNode node, object[] attributes)
        {
            if (value == null) return;
            if (node.NodeName == "Relation")
            {
                node.NodeName = "Relation";
            }
            foreach (Attribute attribute in attributes)
            {
                if (attribute is XmlAttributeAttribute)
                {
                    node.SetAttribute(name, value);
                    return;
                }
                else if (attribute is XmlIgnoreAttribute)
                {
                    return;
                }
                else if (attribute is XmlElementAttribute)
                {
                    Type type = value.GetType();
                    if (TypeHelper.IsGenericList(type))
                    {
                        string itemName = ((XmlElementAttribute)attribute).ElementName;
                        node.AddChild(CreateXmlNode(itemName, value, true));
                    }
                    else
                    {
                        node.AddChild(CreateXmlNode(name, value, false));
                    }
                    return;
                }
            }
            node.AddChild(CreateXmlNode(name, value, false));
        }
    }
}
